package com.beiding.hotcode;

import java.io.File;
import java.util.*;

class MonitorGroup {

    //所有受监管的Java文件
    private Map<String, JavaMonitor> group = new HashMap<>();

    private MyCompiler myCompiler;

    MyFactory myFactory;

    MonitorGroup(MyFactory myFactory) {
        this.myFactory = myFactory;
        myCompiler = new MyCompiler(FileUtils.classpathRootString, myFactory.hotCodeConfig.getEncoding(), FileUtils.classpathRootString);
    }


    //移除一个受监管的java文件
    synchronized void remove(JavaMonitor javaMonitor) {
        if (javaMonitor == null) {
            return;
        }
        this.group.remove(javaMonitor.classname);
        javaMonitor.setMonitorGroup(null);
        //归还暂存
        javaMonitor.setClasspathContext(classpathContextMap.remove(javaMonitor));
        change = true;
    }

    private Map<JavaMonitor, ClasspathContext> classpathContextMap = new HashMap<>();

    synchronized void add(JavaMonitor javaMonitor) {

        if (javaMonitor.monitorGroup != null) {
            throw new RuntimeException("不能将一个类添加到两个不同的分组中");
        }

        if (group.values().contains(javaMonitor)) {
            return;
        }

        classpathContextMap.put(javaMonitor, javaMonitor.getClasspathContext());

        //暂存
        javaMonitor.setClasspathContext(null);

        if (!javaMonitor.myCompiler.charset.equals(this.myCompiler.charset)) {
            System.out.println("类编码字符集(" + javaMonitor.myCompiler.charset + ")与类组编码字符集(" + myCompiler.charset + ")不一致,将采用类组编码字符集(" + myCompiler.charset + ")");
        }

        javaMonitor.checkChange();
        javaMonitor.setMonitorGroup(this);
        this.group.put(javaMonitor.classname, javaMonitor);
        change = true;
    }

    private boolean change = false;

    private MapClassLoader mapClassLoader = new MapClassLoader();

    synchronized boolean checkChange() {

        boolean flag = false;

        if (classpathContext != null) {
            if (classpathContext.checkChange()) {
                flag = true;
            }
        }

        for (JavaMonitor monitor : group.values()) {
            boolean change = monitor.doCheckChange();
            if (change) {
                flag = true;
            }
        }

        return flag || change;
    }

    Class get(String clazz) {
        // return mapClassLoader.loadedClassesMap.get(clazz);
        return group.get(clazz).classFileHolder.checkReloadAndGet();
    }

    private ClasspathContext classpathContext;

    void setClasspathContext(ClasspathContext classpathContext) {
        this.classpathContext = classpathContext;
    }

    ClasspathContext getClasspathContext() {
        return classpathContext;
    }

    //对于已经加载过的不再移除
    void reloadClass() {


        if (group.size() == 0) {
            return;
        }
        MyOrderedClassLoader myClassLoader;

        if (classpathContext == null) {
            myClassLoader = new MyOrderedClassLoader(mapClassLoader);
        } else {
            myClassLoader = new MyOrderedClassLoader(mapClassLoader, classpathContext.getContextClassLoader());
        }

        Set<File> willCompareFile = new HashSet<>();
        Set<JavaMonitor> willReloadClass = new HashSet<>();
        for (JavaMonitor javaMonitor : group.values()) {
            if (javaMonitor.doCheckChange()) {
                willCompareFile.add(javaMonitor.javaFile);
                willReloadClass.add(javaMonitor);
            }
            javaMonitor.classFileHolder.reloadClassLoader(myClassLoader);
        }


        if (willCompareFile.size() == 0) {
            change = false;
            return;
        }

        myCompiler.changeJavaFiles(willCompareFile);

        long currentTimeMillis = System.currentTimeMillis();
        System.out.println("类组  正在编译.....");

        if (myCompiler.compile()) {
            System.out.println("编译完成   耗时:" + (System.currentTimeMillis() - currentTimeMillis) + "ms");

            try {
                for (JavaMonitor j : willReloadClass) {
                    j.classFileHolder.checkReloadAndGet();
                }
            } catch (Throwable e) {
                System.out.println("类加载问题");
            }
        } else {
            System.out.println("编译失败   请检查源码");
        }

        change = false;
        for (JavaMonitor monitor : willReloadClass) {
            monitor.noChange();
        }
    }


}
